package application;

import java.util.HashMap;
import java.util.Map;

/**
 * Implements the functionality of the first NAUI table aka the End-Of-Dive
 * Letter Group Table. For a profile with a single dive, this is the only table
 * needed. In multiple dives, this table determines the user's new letter group.
 * 
 * Single Dive:
 * 
 * <> Input: (fr: user) int depth, int time - assumes that the depth given is on
 * the table
 * 
 * <> Output: char letterGroup
 * 
 * Multiple Dive:
 * 
 * <> Input: (fr: user) int depth, int time - assumes that the depth given is on
 * the table
 * 
 * <> Subsequent Input: (fr: Table 3) int depth, int time - assumes that the
 * depth given is on the table
 * 
 * <> Output: char letterGroup - used by Table 2
 *
 * Other Assumptions:
 * 
 * <> Table 1 assumes that GUI will prevent impossible inputs
 */
public class FirstNAUITable implements NAUITable {
	private char letterGroup; // changes after depth and/or time are changed
	private int depth, time, decompression;
	private Map<Integer, Map<Character, Integer[]>> table; // Table 1 values

	public static final Integer[] UNVIABLE = { -1, -1 };

	/**
	 * Creates Table 1 and sets initial values.
	 */
	public FirstNAUITable() {
		this.depth = 0;
		this.time = 0;
		this.decompression = 0;
		this.letterGroup = DivePlan.INITIAL_GROUP;

		table = new HashMap<Integer, Map<Character, Integer[]>>();
		this.createTable();
	}

	/**
	 * Changes the current depth input for the table. Calculates and sets new
	 * letterGroup based on change.
	 * 
	 * @param depth
	 *            measurement in feet of the user's desired depth OR from Table
	 *            3 for the multiple dive option
	 */
	public void setDepth(int depth) {
		this.setDepthAndTime(depth, this.time);
	}

	/**
	 * Changes the current time input for the table. Calculates and sets new
	 * letterGroup based on change.
	 * 
	 * @param time
	 *            measurement in minutes of the user's desired time OR from
	 *            Table 3 for the multiple dive option
	 */
	public void setTime(int time) {
		this.setDepthAndTime(this.depth, time);
	}

	/**
	 * Changes both depth and time inputs for the table. Calculates and sets new
	 * letterGroup based on change.
	 * 
	 * @param depth
	 *            measurement in feet of the user's desired depth OR from Table
	 *            3 for the multiple dive option
	 * @param time
	 *            measurement in minutes of the user's desired time OR from
	 *            Table 3 for the multiple dive option
	 */
	public void setDepthAndTime(int depth, int time) {
		this.depth = depth;
		this.time = time;

		this.calcOutput();
	}

	/**
	 * Retrieves required decompression time at 15 feet, which is determined by
	 * given depth and time inputs.
	 * 
	 * @return decompression time required
	 */
	public int getDecompression() {
		return this.decompression;
	}

	/**
	 * Calculates and sets letterGroup based on current values for depth and
	 * time inputs.
	 */
	private void calcOutput() {
		Map<Character, Integer[]> value = table.get(this.depth);

		for (int i = 'A'; i <= 'L'; i++) {
			if (value.get((char) i) != null) {
				Integer[] result = value.get((char) i);
				if (result[0] >= this.time) {
					this.letterGroup = (char) i;

					if (result[1] > 0) {
						this.decompression = result[1];
					} else {
						this.decompression = 0;
					}

					break;
				}
			}
		}
	}

	/**
	 * Retrieves currently set letterGroup.
	 * 
	 * If letterGroup is '?', the inputs for letterGroup and/or SIT have NEVER
	 * been given.
	 * 
	 * @return letterGroup calculates
	 */
	public char getLetterGroup() {
		return letterGroup;
	}

	/**
	 * Returns current table
	 * 
	 * @return
	 */
	public Map<Integer, Map<Character, Integer[]>> getTable() {
		return table;
	}

	/**
	 * Creates Table 1.
	 */
	@SuppressWarnings("serial")
	private void createTable() {
		Map<Character, Integer[]> fourty = new HashMap<Character, Integer[]>() {
			{
				put('A', new Integer[] { 5, 0 });
				put('B', new Integer[] { 15, 0 });
				put('C', new Integer[] { 25, 0 });
				put('D', new Integer[] { 30, 0 });
				put('E', new Integer[] { 40, 0 });
				put('F', new Integer[] { 50, 0 });
				put('G', new Integer[] { 70, 0 });
				put('H', new Integer[] { 80, 0 });
				put('I', new Integer[] { 100, 0 });
				put('J', new Integer[] { 110, 0 });
				put('K', new Integer[] { 130, 0 });
				put('L', new Integer[] { 150, 5 });
			}
		};
		Map<Character, Integer[]> fifty = new HashMap<Character, Integer[]>() {
			{
				put('A', null);
				put('B', new Integer[] { 10, 0 });
				put('C', new Integer[] { 15, 0 });
				put('D', new Integer[] { 25, 0 });
				put('E', new Integer[] { 30, 0 });
				put('F', new Integer[] { 40, 0 });
				put('G', new Integer[] { 50, 0 });
				put('H', new Integer[] { 60, 0 });
				put('I', new Integer[] { 70, 0 });
				put('J', new Integer[] { 80, 0 });
				put('K', null);
				put('L', new Integer[] { 100, 5 });
			}
		};
		Map<Character, Integer[]> sixty = new HashMap<Character, Integer[]>() {
			{
				put('A', null);
				put('B', new Integer[] { 10, 0 });
				put('C', new Integer[] { 15, 0 });
				put('D', new Integer[] { 20, 0 });
				put('E', new Integer[] { 25, 0 });
				put('F', new Integer[] { 30, 0 });
				put('G', new Integer[] { 40, 0 });
				put('H', new Integer[] { 50, 0 });
				put('I', new Integer[] { 55, 0 });
				put('J', new Integer[] { 60, 5 });
				put('K', null);
				put('L', new Integer[] { 80, 7 });
			}
		};
		Map<Character, Integer[]> seventy = new HashMap<Character, Integer[]>() {
			{
				put('A', null);
				put('B', new Integer[] { 5, 0 });
				put('C', new Integer[] { 10, 0 });
				put('D', new Integer[] { 15, 0 });
				put('E', new Integer[] { 20, 0 });
				put('F', new Integer[] { 30, 0 });
				put('G', new Integer[] { 35, 0 });
				put('H', new Integer[] { 40, 0 });
				put('I', new Integer[] { 45, 0 });
				put('J', new Integer[] { 50, 5 });
				put('K', new Integer[] { 60, 8 });
				put('L', new Integer[] { 70, 14 });
			}
		};
		Map<Character, Integer[]> eighty = new HashMap<Character, Integer[]>() {
			{
				put('A', null);
				put('B', new Integer[] { 5, 0 });
				put('C', new Integer[] { 10, 0 });
				put('D', new Integer[] { 15, 0 });
				put('E', new Integer[] { 20, 0 });
				put('F', new Integer[] { 25, 0 });
				put('G', new Integer[] { 30, 0 });
				put('H', new Integer[] { 35, 0 });
				put('I', new Integer[] { 40, 5 });
				put('J', null);
				put('K', new Integer[] { 50, 10 });
				put('L', new Integer[] { 60, 17 });
			}
		};
		Map<Character, Integer[]> ninety = new HashMap<Character, Integer[]>() {
			{
				put('A', null);
				put('B', new Integer[] { 5, 0 });
				put('C', new Integer[] { 10, 0 });
				put('D', new Integer[] { 12, 0 });
				put('E', new Integer[] { 15, 0 });
				put('F', new Integer[] { 20, 0 });
				put('G', new Integer[] { 25, 0 });
				put('H', new Integer[] { 30, 5 });
				put('I', null);
				put('J', new Integer[] { 40, 7 });
				put('K', null);
				put('L', new Integer[] { 50, 18 });
			}
		};
		Map<Character, Integer[]> hundred = new HashMap<Character, Integer[]>() {
			{
				put('A', null);
				put('B', new Integer[] { 5, 0 });
				put('C', new Integer[] { 7, 0 });
				put('D', new Integer[] { 10, 0 });
				put('E', new Integer[] { 15, 0 });
				put('F', new Integer[] { 20, 0 });
				put('G', new Integer[] { 22, 0 });
				put('H', new Integer[] { 25, 5 });
				put('I', null);
				put('J', null);
				put('K', new Integer[] { 40, 15 });
				put('L', UNVIABLE);
			}
		};
		Map<Character, Integer[]> hundredten = new HashMap<Character, Integer[]>() {
			{
				put('A', null);
				put('B', null);
				put('C', new Integer[] { 5, 0 });
				put('D', new Integer[] { 10, 0 });
				put('E', new Integer[] { 13, 0 });
				put('F', new Integer[] { 15, 0 });
				put('G', new Integer[] { 20, 5 });
				put('H', null);
				put('I', null);
				put('J', new Integer[] { 30, 7 });
				put('K', UNVIABLE);
				put('L', UNVIABLE);
			}
		};
		Map<Character, Integer[]> hundredtwenty = new HashMap<Character, Integer[]>() {
			{
				put('A', null);
				put('B', null);
				put('C', new Integer[] { 5, 0 });
				put('D', new Integer[] { 10, 0 });
				put('E', new Integer[] { 12, 0 });
				put('F', new Integer[] { 15, 5 });
				put('G', null);
				put('H', null);
				put('I', new Integer[] { 25, 6 });
				put('J', new Integer[] { 30, 14 });
				put('K', UNVIABLE);
				put('L', UNVIABLE);
			}
		};
		Map<Character, Integer[]> hundredthirty = new HashMap<Character, Integer[]>() {
			{
				put('A', null);
				put('B', null);
				put('C', new Integer[] { 5, 0 });
				put('D', new Integer[] { 8, 0 });
				put('E', new Integer[] { 10, 5 });
				put('F', null);
				put('G', null);
				put('H', null);
				put('I', null);
				put('J', new Integer[] { 25, 10 });
				put('K', UNVIABLE);
				put('L', UNVIABLE);
			}
		};
		table.put(40, fourty);
		table.put(50, fifty);
		table.put(60, sixty);
		table.put(70, seventy);
		table.put(80, eighty);
		table.put(90, ninety);
		table.put(100, hundred);
		table.put(110, hundredten);
		table.put(120, hundredtwenty);
		table.put(130, hundredthirty);
	}
}
